package com.swanclouds.model.identity.internal;

import com.swanclouds.api.exceptions.RegionEndpointNotFoundException;
import com.swanclouds.api.identity.EndpointURLResolver;
import com.swanclouds.api.types.ServiceType;
import com.swanclouds.model.identity.Token;
import com.swanclouds.model.identity.URLResolverParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultEndpointURLResolver implements EndpointURLResolver {

    private static final Logger LOG = LoggerFactory.getLogger(DefaultEndpointURLResolver.class);
    private static final Map<Key, String> CACHE = new ConcurrentHashMap<Key, String>();
    private static boolean LEGACY_EP_HANDLING = Boolean.getBoolean(LEGACY_EP_RESOLVING_PROP);
    private String publicHostIP;

    @Override
    public String findURLV1(URLResolverParams p) {

        if (p.type == null) {
            return p.token.getEndpoint();
        }

        Key key = Key.of(p.token.getCacheIdentifier(), p.type);

        String url = CACHE.get(key);

        if (url != null)
            return url;

        url = resolveV3(p);

        if (url != null) {
            CACHE.put(key, url);
            return url;
        } else if (p.region != null)
            throw RegionEndpointNotFoundException.create(p.region, p.type.getServiceName());

        return p.token.getEndpoint();
    }


    private String resolveV3(URLResolverParams p) {
        Token token = p.token;

        //in v3 api, if user has no default project, and token is unscoped, no catalog will be returned
        //then if service is Identity service, should directly return the endpoint back

//        for (com.swancloud.model.identity.v3.Service service : token.getCatalog()) {
//            if (p.type == ServiceType.forName(service.getType()) || p.type == ServiceType.forName(service.getName())) {
//                if (p.perspective == null) {
//                    p.perspective = Facing.PUBLIC;
//                }
//
//                for (com.swancloud.model.identity.v3.Endpoint ep : service.getEndpoints()) {
//
//                    if (matches(ep, p))
//                        return ep.getUrl().toString();
//                }
//            }
//        }


        return null;
    }

    /**
     * Returns <code>true</code> for any endpoint that matches a given
     * {@link URLResolverParams}.
     */
//    private boolean matches(Endpoint endpoint, URLResolverParams p) {
//        boolean matches = false;
//        if (Optional.fromNullable(p.region).isPresent()) {
//            matches &= endpoint.getRegion().equals(p.region);
//        }
//        return matches;
//    }

    /**
     * Gets the endpoint url.
     *
     * @param access the current access data source
     * @param endpoint the endpoint
     * @return the endpoint url
     */
//    private String getEndpointURL(Access access, Endpoint endpoint) {
//        if (LEGACY_EP_HANDLING) {
//            if (endpoint.getAdminURL() != null) {
//                if (getPublicIP(access) != null && !getPublicIP(access).equals(endpoint.getAdminURL().getHost())) {
//                    return endpoint.getAdminURL().toString().replaceAll(endpoint.getAdminURL().getHost(),
//                            getPublicIP(access));
//                }
//                return endpoint.getAdminURL().toString();
//            }
//        }
//        return endpoint.getPublicURL().toString();
//    }

//    private String getPublicIP(Access access) {
//        if (publicHostIP == null) {
//            try {
//                publicHostIP = new URI(access.getEndpoint()).getHost();
//            } catch (URISyntaxException e) {
//                LOG.error(e.getMessage(), e);
//            }
//        }
//        return publicHostIP;
//    }

    private static final class Key {
        private final String uid;
        private final ServiceType type;

        private Key(String uid, ServiceType type) {
            this.type = type;
            this.uid = uid;
        }

        static Key of(String uid, ServiceType type) {
            return new Key(uid , type);
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((type == null) ? 0: type.hashCode());
            result = prime * result + ((uid == null) ? 0: uid.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Key other = (Key) obj;
            if (type != other.type)
                return false;
            if (uid == null) {
                if (other.uid != null)
                    return false;
            } else if (!uid.equals(other.uid))
                return false;
            return true;
        }
    }

}
